package summaries;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import sets.IFuzzySet;

import norms.INorm;
import norms.SNorm;
import norms.TNorm;

import linguistic.Qualifier;
import linguistic.Quantifier;
import linguistic.Quantifier.QuantifierType;
import linguistic.Summarizer;

public class Summary {
	private Quantifier quantifier = null;
	private List<Summarizer> summarizers = new ArrayList<>();
	private List<Qualifier> qualifiers = new ArrayList<>();
	private List<Map<String, Double>> tuples;
	private Results results;
	private String subject = "meczach";
	
	public Summary() {
		
	}
	
	public Summary(Summary summary) {
		// Copy quantifier
		quantifier = summary.getQuantifier();
		
		// Copy qualifiers
		for (int i = 0; i < summary.getQualifiers().size(); ++i) {
			qualifiers.add(summary.getQualifiers().get(i));
		}
		
		// Copy summarisers
		for (int i = 0; i < summary.getSummarizers().size(); ++i) {
			summarizers.add(summary.getSummarizers().get(i));
		}
	}
	
	public void addQuantifier(Quantifier quantifier) {
		this.quantifier = quantifier;
	}
	
	public void addSummarizer(Summarizer summarizer) {
		summarizers.add(summarizer);
	}
	
	public void addQualifier(Qualifier qualifier) {
		qualifiers.add(qualifier);
	}
	
	/**
	 * Put all tuples from database into this summary.
	 * @param tuples
	 */
	public void setTuples(List<Map<String, Double>> tuples) {
		this.tuples = tuples;
		
		// And automatically put tuples to all summarizers and qualifiers
		for (int i = 0; i < summarizers.size(); ++i) {
			summarizers.get(i).putTuples(tuples);
		}
		for (int i = 0; i < qualifiers.size(); ++i) {
			qualifiers.get(i).putTuples(tuples);
		}
	}
	
	public Quantifier getQuantifier() {
		return quantifier;
	}
	
	public List<Summarizer> getSummarizers() {
		return summarizers;
	}
	
	public List<Qualifier> getQualifiers() {
		return qualifiers;
	}
	
	public Results getResults() {
		return results;
	}
	
	
	private String generateSummaryText() {
		String summary = "";
		summary += quantifier.getName() + " " + subject + " ";
		if (qualifiers.size() > 0) {
			summary += "będący/mający ";
			for (Qualifier qualifier : qualifiers) {
				summary += qualifier.getName() + " i ";
			}
			summary = summary.substring(0, summary.length() - 2);
		}
		
		summary += "jest/ma ";
		for (Summarizer summarizer : summarizers) {
			summary += summarizer.getName() + " i ";
		}
		summary = summary.substring(0, summary.length() - 2);
		return summary;
	}
	
	public void show() {
		System.out.println(generateSummaryText());
	}
	
	public String getText() {
		return generateSummaryText();
	}
	
	
	public void qualityMeasures() {
		
		// Let's detect what kind of fuzzy set has been used in Summarizers
		if (summarizers.get(0).getFuzzySet().getType() == IFuzzySet.Type.TYPE1) {
			//
		} else if (summarizers.get(0).getFuzzySet().getType() == IFuzzySet.Type.INTERVAL) {
			//
		} else {
			//
		}
		
		double T1  = T1();
		double T2  = T2();
		double T3  = T3();
		double T4  = T4();
		double T5  = T5();
		double T6  = 0;//T6();
		double T7  = 0;//T7();
		double T8  = T8();
		double T9  = T9();
		double T10 = T10();
		double T11 = T11();
		double T = 0.0;
		
		Results results = new Results(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T, generateSummaryText());
		this.results = results;
	}
	
	/**
	 * Optimal summary
	 * @return
	 */
	private double T() {
		double T = 0.0;
		
		return T;
	}
	
	/**
	 * Degree of truth
	 * @return
	 */
	private double T1() {
		// This is probably the most complex quality measure...
		//   in first step, method has to detect what kind of summary it is
		//		- type 1 only with summarizer(s)
		//		- type 2 with qualifier(s)
		
		double T = 0.0;
		double S = 0.0;
		double W = 0.0;
		double SW = 0.0;
		double r = 0.0;
		INorm norm = new TNorm();
		
		if (qualifiers.size() == 0) {
			// Type 1 only summarizer(s)
			for (Map<String, Double> tuple : tuples) {
				double s = 1.0;
				for (Summarizer summarizer : summarizers) {
					s = norm.min(s, summarizer.membershipValue(tuple));
				}
				S += s;
			}
			r = S;			
		} else {
			// Type 2 with qualifier(s)
			for (Map<String, Double> tuple : tuples) {
				//
				
				double s = 1.0;
				for (Summarizer summarizer : summarizers) {
					s = norm.min(s, summarizer.membershipValue(tuple));
				}
				
				double w = 1.0;
				for (Qualifier qualifier : qualifiers) {
					w = norm.min(w, qualifier.membershipValue(tuple));
				}
				W += w;
				SW += norm.min(s, w);
			}
			r = SW / W;
		}
		
		//System.out.println("r from T1 = " + r);	////////// <-------------------------------------------
		
		if (quantifier.getType() == Quantifier.QuantifierType.ABSOLUTE) {
			T = quantifier.membershipValue(r / 1.0);
		} else {
			T = quantifier.membershipValue(r / (double)tuples.size());
		}
		
		if (qualifiers.size() > 0) {
			T = quantifier.membershipValue(r);
		}
		
		return T;
	}
	
	
	/**
	 * Degree of imprecision
	 * @return
	 */
	private double T2() {
		double T = 0;
		
		for (int i = 0; i < summarizers.size(); ++i) {
			T += summarizers.get(i).getFuzzySet().in().toDouble();
		}
		
		T = Math.pow(T, 1.0 / summarizers.size());
		
		return T;
	}
	
	/**
	 * Degree of covering (only for summaries with qualifiers)
	 * @return
	 */
	private double T3() {
		double T = 0.0;
		INorm norm = new TNorm();
		
		if (qualifiers.size() == 0) {
			T = 0.0;
		} else {
			double t = 0;
			double h = 0;
			for (Map<String, Double> tuple : tuples) {
				double s = 1.0;
				double w = 1.0;
				
				// Compute value of compound summariser
				for (int i = 0; i < summarizers.size(); ++i) {
					s = norm.min(s, summarizers.get(i).membershipValue(tuple));
				}
				
				// Compute value of compound qualifier
				for (int i = 0; i < qualifiers.size(); ++i) {
					w = norm.min(w, qualifiers.get(i).membershipValue(tuple));
				}
				
				if (s > 0 && w > 0) {
					t = t + 1.0;
				}
				
				if (s > 0) {
					h = h + 1.0;
				}
			}
			
			T = t / h;
		}
		
		return T;
	}
	
	/**
	 * Degree of appropriateness
	 * @return
	 */
	private double T4() {
		double T = 1.0;
		
		List<Double> r = new ArrayList<>();
		
		for (int i = 0; i < summarizers.size(); ++i) {
			double g = 0.0;
			for (Map<String, Double> tuple : tuples) {
				if (summarizers.get(i).membershipValue(tuple) > 0) {
					g = g + 1.0;
				}
			}
			g = g / tuples.size();
			r.add(g);
		}
		
		double T3 = T3();
		for (int i = 0; i < r.size(); ++i) {
			T *= r.get(i) - T3;
		}
		
		return Math.abs(T);
	}
	
	/**
	 * Length of summary
	 * @return
	 */
	private double T5() {
		double T = Math.pow(0.5, summarizers.size());
		T = T * 2;
		return T;
	}
	
	/**
	 * Degree of quantifier imprecision
	 * @return
	 */
	private double T6() {
		// TODO: implementation needed
		return 9.0;
	}
	
	/**
	 * Degree of quantifier cardinality
	 * @return
	 */
	private double T7() {
		// TODO: implementation needed
		return 0.0;
	}
	
	/**
	 * Degree of summariser cardinality
	 * @return
	 */
	private double T8() {
		double T = 1.0;
		double S = 0.0;
		double X = 0.0;
		
		for (Summarizer summarizer : summarizers) {
			S = summarizer.getFuzzySet().card().toDouble();
			X = tuples.size();
			
			T *= S / X;
		}
		T = Math.pow(T, 1.0 / summarizers.size());
		T = 1 - T;
		return T;
	}
	
	/**
	 * Degree of quantifier imprecision
	 * @return
	 */
	private double T9() {
		double T = 1.0;
		
		if (qualifiers.size() == 0) {
			T = 0.0;
		} else if (qualifiers.size() == 1) {
			T = qualifiers.get(0).getFuzzySet().in().toDouble();
			T = 1 - T;
		} else {
			for (Qualifier qualifier : qualifiers) {
				T *= qualifier.getFuzzySet().in().toDouble();
			}
			T = Math.pow(T, (1.0 / qualifiers.size()));
			T = 1 - T;
		}
		return T;
	}
	
	/**
	 * Degree of qualifier cardinality
	 * @return
	 */
	private double T10() {
		double T = 1.0;
		double W = 0.0;
		double X = 0.0;
		
		if (qualifiers.size() == 0) {
			T = 0.0;
		} else if (qualifiers.size() == 1) {
			W = qualifiers.get(0).getFuzzySet().card().toDouble();
			X = tuples.size();
			
			T = W / X;
			T = 1 - T;
		} else {
			for (Qualifier qualifier : qualifiers) {
				W = qualifier.getFuzzySet().card().toDouble();
				X = tuples.size();
				T *= W / X;
			}
			T = Math.pow(T, 1.0 / qualifiers.size());
			T = 1 - T;
		}
		
		return T;
	}
	
	/**
	 * Length of qualifier
	 * @return
	 */
	private double T11() {
		double T = Math.pow(0.5, qualifiers.size());
		if (qualifiers.size() > 0) {
			T = T * 2;
			return T;
		} else {
			return 0.0;
		}
	}
}
